package org.jackysoft.service.impl;

import java.io.File;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.annotation.Resource;

import org.hibernate.SQLQuery;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.jackysoft.config.CommonConfiguration;
import org.jackysoft.data.SortInfo;
import org.jackysoft.entity.ArchiveDocument;
import org.jackysoft.entity.CustomOrganization;
import org.jackysoft.entity.HibernateWorkflowEntry;
import org.jackysoft.entity.OfficeDocument;
import org.jackysoft.entity.User;
import org.jackysoft.entity.Variable;
import org.jackysoft.entity.ViewFile;
import org.jackysoft.service.AbstractStringIdServiceImpl;
import org.jackysoft.service.ArchiveDocumentService;
import org.jackysoft.service.UserService;
import org.jackysoft.service.VariableService;
import org.jackysoft.service.ViewFileService;
import org.jackysoft.service.WorkflowService;
import org.jackysoft.util.DateUtils;
import org.jackysoft.util.FileInfo;
import org.jackysoft.util.HttpUtil;
import org.jackysoft.util.IOUtils;
import org.jackysoft.util.Office2PDF;
import org.jackysoft.util.SpringSecurityUtils;
import org.jackysoft.util.StringUtils;
import org.jackysoft.util.WorkflowConstant;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

@Service
@Transactional
public class ViewFileServiceImpl extends AbstractStringIdServiceImpl<ViewFile>
		implements ViewFileService {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6450858601546131399L;

	private UserService userService;
	private WorkflowService workflowService;
	protected VariableService variableService;	
	private ArchiveDocumentService acrhiveService;
	public ViewFileServiceImpl() {
		super(ViewFile.class);
	}

	@Override
	public void create(ViewFile f) throws DataAccessException {
		Session session = getSession();
		ViewFile vf = collectFile(f, session);
		session.save(vf);
	}



	@Override
	public void remove(Collection<ViewFile> fs)
			throws DataAccessException {
		int count = 0;
		for (ViewFile vf : fs) {
			getSession().delete(vf);
			if (++count % 10 == 0) {
				getSession().flush();
				getSession().clear();
			}
		}

		// this.removeBean(f);
	}


	@Override
	public ViewFile reSend(ViewFile f) throws DataAccessException {
		ViewFile vf = get(f.getId());
		Set<User> readers = Sets.newHashSet();
		readers.addAll(vf.getReaders());
		readers.addAll(f.getReaders());
		vf.setReaders(readers);
		update(vf);
		return vf;
	}

	@Override
	public Boolean canSendable(ViewFile f) throws DataAccessException {
		f = get(f.getId());
		User cuser = SpringSecurityUtils.getCurrentUser();
		return f.getOwners().contains(cuser);
	}

	@Resource
	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public UserService getUserService() {
		return userService;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
	public void releaseUser(User owner) throws DataAccessException {
		log.info("清除查看文件用户....");
		Session session = getSession();

		Collection<ViewFile> fs = session.createQuery("from ViewFile ").list();
		for (ViewFile f : fs) {
			log.info("\t清除用户[ " + owner.getUsername() + " ] 从文件  "
					+ f.getName() + "...");
			log.info("\t\t清除所有权[ " + owner.getUsername() + " ] 从文件  "
					+ f.getName() + "...");
			f.getOwners().remove(owner);
			log.info("\t\t清除阅读权[ " + owner.getUsername() + " ] 从文件  "
					+ f.getName() + "...");
			f.getReaders().remove(owner);
			log.info("\t\t清除阅读记录[ " + owner.getUsername() + " ] 从文件  "
					+ f.getName() + "...");
			f.getReadereds().remove(owner);
			session.update(f);
			log.info("\t清除用户信息 [" + owner.getUsername() + "] 从文件 "
					+ f.getName() + "...[OK]");
		}
		log.info("清除用户查看文件....[OK]");
	}

	@Override
	public void markReader(ViewFile f, User user) throws DataAccessException {
		boolean hasread = hasReaded(f, user);
		if (hasread)
			return;
		ViewFile vf = get(f.getId());
		vf.getReadereds().add(user);
		update(vf);
	}

	@Override
	public boolean hasReaded(ViewFile f, User user) throws DataAccessException {
		ViewFile vf = get(f.getId());

		boolean hasread = false;
		for (User u : vf.getReadereds()) {
			if (u.getUsername().equalsIgnoreCase(user.getUsername())) {
				return true;
			}
		}
		return hasread;
	}

	/**
	 * 下发工作流中的文件内容
	 * @param  f 文件对象
	 * @param  workflowId 工作流编号
	 * */
	@Override
	public void create(ViewFile f, Long workflowId)
			throws DataAccessException, InterruptedException, ExecutionException {
		Session session = getSession();
		ViewFile bean = collectFile(f, session);
		HibernateWorkflowEntry entry = (HibernateWorkflowEntry) workflowService
				.findEntry(workflowId);
		OfficeDocument office = entry.getOfficeDocument();
		byte[] data = office.getCollateData();
		Office2PDF wpf = new Office2PDF(getConfig().getPdfServer(),getConfig().getPdfServerPort(), "doc", data);
		ExecutorService executor = Executors.newSingleThreadExecutor();
		Future<String> future = executor.submit(wpf);
		String name = future.get() + ".pdf";
		String urlPrefix = "/attachment/" + name;
		String realPath = File.separator+"attachment"+File.separator+name;
		bean.setRealName(name);
		bean.setRealPath(realPath);
		bean.setUrlPrefix(urlPrefix);
		bean.setSize(data.length);
		bean.setExtision("pdf");
		bean.setUpdateDate(Calendar.getInstance());
		bean.setMimeType(HttpUtil.extToMimeType("pdf"));
		Variable var = variableService.get(workflowId,WorkflowConstant.WORKFLOW_TITLE.toString());
		String fileName = var != null ? var.getValue() : "未命名";
		bean.setName(fileName);
		User owner = SpringSecurityUtils.getCurrentUser();
		ArchiveDocument abean = new ArchiveDocument();
		abean.setExecutor(owner);
		abean.setCreateDate(Calendar.getInstance());
		abean.setPdfFile(name);
		abean.setName(fileName);
		acrhiveService.create(abean);
		entry.setArchiveDocument(abean);
		session.saveOrUpdate(entry);
		session.save(bean);

	}

	protected ViewFile collectFile(ViewFile f, Session session) {
		CustomOrganization corg = f.getCustomOrganization();

		Set<User> users = Sets.newHashSet();
		if (corg != null) {
			CustomOrganization corg2 = (CustomOrganization) session.get(
					CustomOrganization.class, corg.getId());
			if (corg2 != null) {
				users.addAll(corg2.getUsers());
			}
		}
		User user = SpringSecurityUtils.getCurrentUser();
		users.add(user);
		f.setManager(user);
		users.addAll(userService.gets(f.getDepartments()));
		users.addAll(userService.getsByRoles(f.getRoles()));
		users.addAll(f.getOwners());
		f.setOwners(users);
		return f;
	}

	@Resource
	public void setWorkflowService(WorkflowService workflowService) {
		this.workflowService = workflowService;
	}

	

	@Resource
	public void setVariableService(VariableService variableService) {
		this.variableService = variableService;
	}

	@Override
	protected int listDataSize(SortInfo sortInfo, Map<String, Object> smap) {
		String sql = "select count(*) from("
				+ "	select fa.* from viewfile fa "
				+ "	join user_file_reader ur on ur.readers_name=:usernamea and fa.id = ur.ViewFile_id"
				+ "	union "
				+ "	select fb.* from viewfile fb"
				+ "	join user_file_owner uo on uo.owners_name=:usernameb and fb.id = uo.ViewFile_id"
				+ "	) as ff ";
		return parseSize(sql);
	}

	@Override
	protected List<ViewFile> listData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {
		String sql = "select distinct f.id,f.realName,f.name,f.size,f.extision"
				+ ",f.mimeType,f.updateDate,f.manager_name"
				+ "	from("
				+ "	select fa.* from viewfile fa "
				+ "	join user_file_reader ur on ur.readers_name=:usernamea and fa.id = ur.ViewFile_id"
				+ "	union "
				+ "	select fb.* from viewfile fb"
				+ "	join user_file_owner uo on uo.owners_name=:usernameb and fb.id = uo.ViewFile_id"
				+ "  ) as f" + "  order by f.updateDate desc";
		return parseList(sql, start, size, listAll);
	}

	@Override
	protected List<ViewFile> searchData(int start, int size, boolean listAll,
			SortInfo sortInfo, Map<String, Object> smap) {
		ViewFile bean = (ViewFile) smap.get("bean");
		String sql = "select distinct f.id,f.realName,f.name,f.size,f.extision"
				+ ",f.mimeType,f.updateDate,f.manager_name"
				+ "  from viewfile  f "
				+ "  left join user_file_owner ou on ou.viewfile_id=f.id and ou.owners_name=:usernamea"
				+ "  left join user_file_reader ru on ru.viewfile_id=f.id and ru.readers_name=:usernameb"
				+ " where f.manager_name=:usernamec "
				+ (StringUtils.nullableString(bean.getName()) ? ""
						: " and f.name like '%" + bean.getName() + "%'")
				+ (bean.getUpdateDate() == null ? ""
						: " and year(f.updateDate)="
								+ DateUtils.getYear(bean.getUpdateDate())
								+ " and month(f.updateDate)="
								+ DateUtils.getMonth(bean.getUpdateDate()))
				+ " and date(f.updateDate)="
				+ DateUtils.getDate(bean.getUpdateDate())
				+ " order by updateDate desc";
		return parseList(sql, start, size, listAll);
	}

	@Override
	protected int searchDataSize(SortInfo sortInfo, Map<String, Object> smap) {
		ViewFile bean = (ViewFile) smap.get("bean");
		String sql = "select  count(distinct f.id)  count  from viewfile f "
				+ " left join user_file_owner ou on ou.viewfile_id=f.id and ou.owners_name=:usernamea "
				+ " left join user_file_reader ru on ru.viewfile_id=f.id and ru.readers_name=:usernameb"
				+ " where  f.manager_name=:usernamec"
				+ (StringUtils.nullableString(bean.getName()) ? ""
						: " and f.name like '%" + bean.getName() + "%'")
				+ (bean.getUpdateDate() == null ? ""
						: " and year(f.updateDate)="
								+ DateUtils.getYear(bean.getUpdateDate())
								+ " and month(f.updateDate)="
								+ DateUtils.getMonth(bean.getUpdateDate()))
				+ " and date(f.updateDate)="
				+ DateUtils.getDate(bean.getUpdateDate());
		return parseSize(sql);
	}

	List<ViewFile> parseList(String sql, int start, int size, boolean listAll) {
		String username = SpringSecurityUtils.getCurrentUserName();
		Session session = getSession();
		String qs = sql;
		SQLQuery q = session.createSQLQuery(qs);
		q.setString("usernamea", username);
		q.setString("usernameb", username);
		// q.setString("usernamec", username);
		if (!listAll) {
			q.setFirstResult(start);
			q.setMaxResults(size);
		}
		List<ViewFile> list = Lists.newArrayList();
		ScrollableResults rsts = q.scroll();
		while (rsts.next()) {

			ViewFile f = new ViewFile();
			Object[] row = rsts.get();
			f.setId((String) row[0]);
			f.setRealName((String) row[1]);
			f.setName((String) row[2]);
			BigInteger bi = (BigInteger) row[3];
			f.setSize(bi.longValue());
			f.setExtision((String) row[4]);
			f.setMimeType((String) row[5]);
			Timestamp ts = (Timestamp) row[6];
			Calendar cd = Calendar.getInstance();
			cd.setTimeInMillis(ts.getTime());
			f.setUpdateDate(cd);
			User u = new User();
			u.setUsername((String) row[7]);
			f.setManager(u);
			list.add(f);
		}
		return list;
	}

	int parseSize(String sql) {
		Object o = null;
		String username = SpringSecurityUtils.getCurrentUserName();
		Session session = getSession();
		String qs = sql;
		SQLQuery q = session.createSQLQuery(qs);
		q.setString("usernamea", username);
		q.setString("usernameb", username);
		o = q.uniqueResult();
		return o == null ? 0 : Integer.parseInt(o.toString());
	}

	@Override
	public void dispatch(boolean uploaded, ViewFile bean, Long workflowId,
			File upLoad, String upLoadFileName) throws InterruptedException,
			ExecutionException {
		
		if (uploaded) {// 下发上传的文件，可能是pdf ，也可能是 office 格式
			String extName = StringUtils.splitPostFileName(upLoadFileName);
			bean.setExtision("pdf");
			bean.setMimeType(HttpUtil.extToMimeType(bean.getExtision()));
			bean.setUpdateDate(Calendar.getInstance());
			bean.setSize(upLoad.length());
			bean.setName(StringUtils.splitPreFileName(upLoadFileName));
			if (extName.equalsIgnoreCase(CommonConfiguration.DOC)	|| extName.equalsIgnoreCase(CommonConfiguration.DOCX))
			{

				byte[] data = IOUtils.toByteArray(upLoad);
				Office2PDF wpf = new Office2PDF(getConfig().getPdfServer(),getConfig().getPdfServerPort(), extName, data);
				ExecutorService executor = Executors.newSingleThreadExecutor();
				Future<String> future = executor.submit(wpf);
				String name = future.get() + ".pdf";
				String urlprefix = "/attachment/" + name;
				bean.setUrlPrefix(urlprefix);
				bean.setRealName(name);
				String realName = File.separator+"attachment"+File.separator+name;
				bean.setRealPath(realName);
				
			}
			if (extName.equalsIgnoreCase(CommonConfiguration.PDF)) {
				UUID uid = UUID.randomUUID();
				String name = uid.toString() + "." + extName;
				bean.setRealName(name);
				String urlPrefix = "/attachment/" + name;
				bean.setRealPath(urlPrefix);
				bean.setUrlPrefix(urlPrefix);
				String realName = File.separator+"attachment"+File.separator+name;
				bean.setRealPath(realName);
				IOUtils.saveFile(upLoad, new File(bean.getRealPath()));
			}
			create(bean);
		} else {// 下发工作流管理中的文档
			create(bean, workflowId);
		}

	}

	
    
	@Override
	public FileInfo download(String id) throws Exception {
		ViewFile bean = get(id);
		FileInfo info = new FileInfo();
		byte[] content = IOUtils.toByteArray(new File(bean.getRealPath()));
		info.setData(content);
		info.setFileType(bean.getExtision());
		info.setRealPath(bean.getRealPath());
		info.setTitle(bean.getName());		
		return info;
	}

	@Resource
	public void setAcrhiveService(ArchiveDocumentService acrhiveService) {
		this.acrhiveService = acrhiveService;	    
	}

}
